#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <net/if.h>
#include <time.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>

#include "network.h"
#include "settings_manager.h"

static int network_add_route(char *dest, char *mask, char *gw, char *dev)
{
	if(NULL == dest || NULL == mask || NULL == dev)
	{
		return -1;
	}

	char command[COMMON_STRING_LENGTH] = {0};
	if(gw == NULL || strlen(gw) == 0)
	{
		snprintf(command, COMMON_STRING_LENGTH, "route add -net %s netmask %s dev %s", dest, mask, dev);
	}
	else
	{
		snprintf(command, COMMON_STRING_LENGTH, "route add -net %s netmask %s gw %s dev %s", dest, mask, gw, dev);
	}	
	printf("%s\n", command);
	system(command);
	return 0;
}

static int network_del_route(char *dest, char *mask, char *gw, char *dev)
{
	if(NULL == dest || NULL == mask)
	{
		return -1;
	}

	char command[COMMON_STRING_LENGTH] = {0};
	sprintf(command, "route del -net %s netmask %s", dest, mask);
	printf("%s\n", command);
	system(command);
	return 0;
}

static int set_ip_mask(char *dev, char *ip, char *mask)
{
	if(NULL == dev || NULL == ip || NULL == mask)
	{
		return -1;
	}

	char command[COMMON_STRING_LENGTH] = {0};
	snprintf(command, COMMON_STRING_LENGTH, "ifconfig %s up; ifconfig %s %s netmask %s", dev, dev, ip, mask);
	printf("%s\n", command);
	system(command);

	return 0;
}

static int set_default_gw(char *gw)
{
	if(NULL == gw)
	{
		return -1;
	}

	char command[COMMON_STRING_LENGTH] = {0};
	snprintf(command, COMMON_STRING_LENGTH, "route del default 2>/dev/null; route add default gw %s", gw);
	printf("%s\n", command);
	system(command);

	return 0;
}

static int get_mac_address(char *dev, char *mac_addr)
{
	if(NULL == dev || NULL == mac_addr)
	{
		return -1;
	}
	int fd;
	struct ifreq ifr;
	fd = socket(AF_INET, SOCK_DGRAM, 0);
	ifr.ifr_addr.sa_family = AF_INET;
	strcpy(ifr.ifr_name, dev);
	ioctl(fd, SIOCGIFHWADDR, &ifr);
	sprintf(mac_addr, "%02X:%02X:%02X:%02X:%02X:%02X",
		(unsigned char)ifr.ifr_hwaddr.sa_data[0],
		(unsigned char)ifr.ifr_hwaddr.sa_data[1],
		(unsigned char)ifr.ifr_hwaddr.sa_data[2],
		(unsigned char)ifr.ifr_hwaddr.sa_data[3],
		(unsigned char)ifr.ifr_hwaddr.sa_data[4],
		(unsigned char)ifr.ifr_hwaddr.sa_data[5]
		);
	close(fd);
	return 0;
}

static int set_mac_address(char *dev, char *mac_addr)
{
	if(NULL == dev || NULL == mac_addr )
	{
		return -1;
	}
	char command[COMMON_STRING_LENGTH] = {0};
	snprintf(command, COMMON_STRING_LENGTH, "ifconfig %s down; ifconfig %s hw ether %s; ifconfig %s up", dev, dev, mac_addr, dev);
	printf("%s\n", command);
	system(command);
	return 0;
}


static void *adapter_hotplug_check_thread_proc(void *argv)
{
	int fd, retval;
	char buf[COMMON_STRING_LENGTH * 100] = { 0 };
	int len = COMMON_STRING_LENGTH * 100;
	int size = 0;
	
	struct sockaddr_nl addr;
	struct nlmsghdr *nh;
	struct ifinfomsg *ifinfo;
	struct rtattr *attr;
	char adapter[1024] = { 0 };
	
	pthread_detach(pthread_self());

	printf("Enter %s thread\n", __func__);
	memset(&addr, 0, sizeof(addr));
	addr.nl_family = AF_NETLINK;
	addr.nl_pid = getpid();
	addr.nl_groups = 1;

	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
	setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len));
	memset(&addr, 0, sizeof(addr));
	addr.nl_family = AF_NETLINK;
	addr.nl_groups = RTNLGRP_LINK;
	bind(fd, (struct sockaddr*)&addr, sizeof(addr));
	while ((retval = read(fd, buf, COMMON_STRING_LENGTH * 100)) > 0)
	{
		for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, retval); nh = NLMSG_NEXT(nh, retval))
		{
			char msg[200];
			if (nh->nlmsg_type == NLMSG_DONE)
				break;
			else if (nh->nlmsg_type == NLMSG_ERROR)
				break;
			else if (nh->nlmsg_type != RTM_NEWLINK)
				continue;
			ifinfo = NLMSG_DATA(nh);

			//sprintf(msg, "%u: %s", ifinfo->ifi_index,(ifinfo->ifi_flags & IFF_RUNNING) ? "up" : "down");
			attr = (struct rtattr*)(((char*)nh) + NLMSG_SPACE(sizeof(*ifinfo)));
			len = nh->nlmsg_len - NLMSG_SPACE(sizeof(*ifinfo));
			for (; RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
			{
				if (attr->rta_type == IFLA_IFNAME)
				{
					if (strstr((char*)RTA_DATA(attr), NET_DEV_NAME_CTRL) == NULL)
					{
						continue;
					}

					network_config(NET_DEV_CRTL);
					usleep(1200 * 1000);
				
					printf("%s:%s detect\n", __func__, NET_DEV_NAME_CTRL);
					break;
				}
			}
		}
	}
	printf("Exit %s thread", __func__);
	return NULL;
}

void get_ctrl_mac_address(char *mac)
{
	get_mac_address(NET_DEV_NAME_CTRL, mac);
}

int network_config(netIntf intf)
{
	char ip[IP_LEN] = {0};
	char mask[IP_LEN] = {0};
	char gateway[IP_LEN] = {0};
	char *dev = NULL;

	if(intf == NET_DEV_CRTL)
	{
		dev = NET_DEV_NAME_CTRL;
		get_ctrl_ipaddr_setting(ip);
		get_ctrl_mask_setting(mask);
		printf("<%s> intf:CTRL,  dev:%s, ip:%s, mask:%s\n", __func__, dev, ip, mask);
		set_ip_mask(dev, ip, mask);
	}
	else if(intf == NET_DEV_STREAM)
	{
		dev = NET_DEV_NAME_STREAM;
		if (get_ip_addressing_setting(NULL) == IP_ADDRESSING_Static)
		{
			get_stream_ipaddr_setting(ip);
			get_stream_mask_setting(mask);
			get_stream_gateway_setting(gateway);
			printf("<%s> intf:STREAM, dev:%s, ip:%s, mask:%s, gw:%s\n", __func__, dev, ip, mask, gateway);
			set_ip_mask(dev, ip, mask);
			set_default_gw(gateway);
		}
		else//DHCP
		{
			char dhcp_cmd[128];
			sprintf(dhcp_cmd, "udhcpc -t 1 -q -i %s > dhcp_msg_name 2>&1", NET_DEV_NAME_STREAM);
			system("killall udhcpc");
			system(dhcp_cmd);

			char ipstr[IP_LEN];
			memset(ipstr, 0, IP_LEN);
			if (GetLocalIp(ipstr) == -1)
			{
				printf("[Error]:Local Stream IP config error\n");
				return -1;
			}
			else
			{
				set_stream_ipaddr_setting(ipstr);
			}
			memset(ipstr, 0, IP_LEN);
			if (GetLocalNetMask(ipstr) == -1)
			{
				printf("[Error]:Local Stream Mask config error\n");
				return -1;
			}
			else
			{
				set_stream_mask_setting(ipstr);
			}
			memset(ipstr, 0, IP_LEN);
			if (GetGateWay(ipstr) == -1)
			{
				printf("[Error]:Local Stream gateway config error\n");
				return -1;
			}
			else
			{
				set_stream_gateway_setting(ipstr);
			}
		}
	}
	else
	{
		assert(0);
	}
	return 0;
}

int network_init()
{
	char mac[20];
	get_mac_address_setting(mac);
	set_mac_address(NET_DEV_NAME_STREAM, mac);
	network_config(NET_DEV_STREAM);
	network_config(NET_DEV_CRTL);
	return 0;
}

int test_ping_target_ip(char * ip_addr, char *result, int len)
{
	int ret = 0;
	FILE *fp = NULL;
	char cmd[100] = "ping ";
	char buffer[200] = {0};
	strcat(cmd, ip_addr);
	strcat(cmd, " -w 1");
	int success = 0;
	int length = 0, index = 0;
	
	fp = popen(cmd, "r");
	while(fgets(buffer, sizeof(buffer), fp) != NULL)
	{
		if (strstr(buffer, "PING") || strstr(buffer, "statistics") || strstr(buffer, "transmitted"))
			continue;
		else
		{
			success = 1;
			strncpy(result, buffer, len);
			break;
		}
	}

	length = strlen(result);
	for (index = 0; index < length; index++)
	{
		if (result[index] == '\n')
		{
			result[index] = 0;
			break;
		}
	}
	if (success != 1 || result[0] == 0)
		snprintf(result, len, "Ping %s error", ip_addr);
	
	pclose(fp);
	return 0;
}

int adapter_check_start(void)
{	
	pthread_t id;
	int ret = 0;
	ret = pthread_create(&id, NULL, adapter_hotplug_check_thread_proc, NULL);
	if (ret != 0)
	{
		printf("%s:pthread_create network_check_state error\n", __func__);
		return -1;
	}
	return 0;
}

//get eth0 ip
int GetLocalIp(char *ipaddr)
{
	int sock_get_ip;
	struct   sockaddr_in *sin;
	struct   ifreq ifr_ip;
	int ip_arr[4];
	char *p_ip = NULL;
	int i = 0;
	if ((sock_get_ip = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		return -1;
	}
	memset(&ifr_ip, 0, sizeof(ifr_ip));
	strncpy(ifr_ip.ifr_name, NET_DEV_NAME_STREAM, sizeof(ifr_ip.ifr_name) - 1);
	if (ioctl(sock_get_ip, SIOCGIFADDR, &ifr_ip) < 0)
	{
		return -1;
	}
	sin = (struct sockaddr_in *)&ifr_ip.ifr_addr;
	strcpy(ipaddr, inet_ntoa(sin->sin_addr));
	for (p_ip = strtok(ipaddr, "."); p_ip != NULL; p_ip = strtok(NULL, "."))
	{
		ip_arr[i] = atoi(p_ip);
		i++;
	}
	memset(ipaddr, '\0', 40);
	sprintf(ipaddr, "%03d.%03d.%03d.%03d", ip_arr[0], ip_arr[1], ip_arr[2], ip_arr[3]);

	close(sock_get_ip);
	return 0;
}
//get mask eth0
int GetLocalNetMask(char *netmask_addr)
{
	int sock_netmask;
	struct ifreq ifr_mask;
	struct sockaddr_in *net_mask;
	int ip_arr[4];
	char *p_ip = NULL;
	int i = 0;
	sock_netmask = socket(AF_INET, SOCK_STREAM, 0);
	if (sock_netmask == -1)
	{
		return -1;
	}
	memset(&ifr_mask, 0, sizeof(ifr_mask));
	strncpy(ifr_mask.ifr_name, NET_DEV_NAME_STREAM, sizeof(ifr_mask.ifr_name) - 1);
	if ((ioctl(sock_netmask, SIOCGIFNETMASK, &ifr_mask)) < 0)
	{
		return -1;
	}
	net_mask = (struct sockaddr_in *)&(ifr_mask.ifr_netmask);
	strcpy(netmask_addr, inet_ntoa(net_mask->sin_addr));
	for (p_ip = strtok(netmask_addr, "."); p_ip != NULL; p_ip = strtok(NULL, "."))
	{
		ip_arr[i] = atoi(p_ip);
		i++;
	}
	memset(netmask_addr, '\0', 40);
	sprintf(netmask_addr, "%03d.%03d.%03d.%03d", ip_arr[0], ip_arr[1], ip_arr[2], ip_arr[3]);

	close(sock_netmask);
	return 0;
}
//get gateway eth0
int GetGateWay(char *gateway)
{
	FILE *fp;
	char buf[512];
	char cmd[128];
	char *tmp;
	int ip_arr[4];
	char *p_ip = NULL;
	int i = 0;
	strcpy(cmd, "ip route");
	fp = popen(cmd, "r");
	if (NULL == fp)
	{
		return -1;
	}
	while (fgets(buf, sizeof(buf), fp) != NULL)
	{
		tmp = buf;
		while (*tmp && isspace(*tmp))
			++tmp;
		if (strncmp(tmp, "default", strlen("default")) == 0)
			break;
	}
	sscanf(buf, "%*s%*s%s", gateway);
	//printf("[1]default buf %s gateway:%s\n", buf, gateway);
	for (p_ip = strtok(gateway, "."); p_ip != NULL; p_ip = strtok(NULL, "."))
	{
		ip_arr[i] = atoi(p_ip);
		i++;
	}
	memset(gateway, '\0', 40);
	sprintf(gateway, "%03d.%03d.%03d.%03d", ip_arr[0], ip_arr[1], ip_arr[2], ip_arr[3]);
	//printf("[2]default gateway:%s\n", gateway);
	pclose(fp);
	return 0;
}